Skip to content

Commit

Permalink
EasyRSA redesign
Browse files Browse the repository at this point in the history
  • Loading branch information
paragonie-security committed May 18, 2016
1 parent 58dde26 commit c36648a
Show file tree
Hide file tree
Showing 12 changed files with 241 additions and 70 deletions.
2 changes: 0 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ php:

- "7.0"
- "5.6"
- "5.5"
- "5.4"
- "hhvm"

sudo: false
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ EasyRSA is MIT licensed and brought to you by the secure PHP development team at
You can generate 2048-bit keys (or larger) using EasyRSA. The default size is 2048.

```php
use \ParagonIE\EasyRSA\EasyRSA;
use \ParagonIE\EasyRSA\KeyPair;

KeyPair::generateKeyPair(4096);
$secretKey = $keyPair->getPrivateKey();
$publicKey = $keyPair->getPublicKey();

list($secretKey, $publicKey) = EasyRSA::generateKeyPair(4096);
```

### Encrypting/Decrypting a Message

```php
use \ParagonIE\EasyRSA\EasyRSA;

$ciphertext = EasyRSA::encrypt($message, $publicKey);

$plaintext = EasyRSA::decrypt($ciphertext, $secretKey);
Expand All @@ -47,6 +52,8 @@ $plaintext = EasyRSA::decrypt($ciphertext, $secretKey);
### Signing/Verifying a Message

```php
use \ParagonIE\EasyRSA\EasyRSA;

$signature = EasyRSA::sign($message, $secretKey);

if (EasyRSA::verify($message, $signature, $publicKey)) {
Expand Down
6 changes: 4 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
},
"require": {
"phpseclib/phpseclib": "^2.0",
"defuse/php-encryption": "^1.2",
"sarciszewski/php-future": "^0.4"
"defuse/php-encryption": "^2.0",
"paragonie/constant_time_encoding": "^1|^2",
"paragonie/random_compat": "^1|^2",
"sarciszewski/php-future": "^0"
},
"require-dev": {
"phpunit/phpunit": "4.*|5.*"
Expand Down
2 changes: 1 addition & 1 deletion phpunit.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash

php vendor/bin/phpunit test
vendor/bin/phpunit test
if [ $? -ne 0 ]; then
# Test failure
exit 1
Expand Down
68 changes: 27 additions & 41 deletions src/EasyRSA.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,18 @@
// PHPSecLib:
use \phpseclib\Crypt\RSA;
// defuse/php-encryption:
use \Crypto;
use \ParagonIE\ConstantTime\Base64;
use \Defuse\Crypto\Key;
use \Defuse\Crypto\Crypto;
// Typed Exceptions:
use \ParagonIE\EasyRSA\Exception\InvalidChecksumException;
use \ParagonIE\EasyRSA\Exception\InvalidCiphertextException;
use \ParagonIE\EasyRSA\Exception\InvalidKeyException;

class EasyRSA implements EasyRSAInterface
{
const SEPARATOR = '$';
const VERSION_TAG = "EzR1";

/**
* Generate a private/public RSA key pair
*
* @return array [private, public]
*/
public static function generateKeyPair($size = 2048)
{
if ($size < 2048) {
throw new InvalidKeyException('Key size must be at least 2048 bits.');
}
$rsa = new RSA();
$keypair = $rsa->createKey($size);
return array(
$keypair['privatekey'],
$keypair['publickey']
);
}


/**
* Encrypt a message with defuse/php-encryption, using an ephemeral key,
* then encrypt the key with RSA.
Expand All @@ -42,19 +25,19 @@ public static function generateKeyPair($size = 2048)
*
* @return string
*/
public static function encrypt($plaintext, $rsaPublicKey)
public static function encrypt($plaintext, PublicKey $rsaPublicKey)
{
// Random encryption key
$ephemeral = Crypto::createNewRandomKey();
$ephemeral = Key::createNewRandomKey();

// Encrypt the actual message
$symmetric = \base64_encode(
Crypto::encrypt($plaintext, $ephemeral)
$symmetric = Base64::encode(
Crypto::encrypt($plaintext, $ephemeral, true)
);

// Use RSA to encrypt the encryption key
$storeKey = \base64_encode(
self::rsaEncrypt($ephemeral, $rsaPublicKey)
self::rsaEncrypt($ephemeral->saveToAsciiSafeString(), $rsaPublicKey)
);

$packaged = \implode(self::SEPARATOR,
Expand Down Expand Up @@ -85,7 +68,7 @@ public static function encrypt($plaintext, $rsaPublicKey)
*
* @return string
*/
public static function decrypt($ciphertext, $rsaPrivateKey)
public static function decrypt($ciphertext, PrivateKey $rsaPrivateKey)
{
$split = explode(self::SEPARATOR, $ciphertext);
if (\count($split) !== 4) {
Expand All @@ -103,13 +86,16 @@ public static function decrypt($ciphertext, $rsaPrivateKey)
throw new InvalidChecksumException('Invalid checksum');
}

$key = self::rsaDecrypt(
\base64_decode($split[1]),
$rsaPrivateKey
$key = Key::loadFromAsciiSafeString(
self::rsaDecrypt(
Base64::decode($split[1]),
$rsaPrivateKey
)
);
return Crypto::Decrypt(
\base64_decode($split[2]),
$key
Base64::decode($split[2]),
$key,
true
);
}

Expand All @@ -120,13 +106,13 @@ public static function decrypt($ciphertext, $rsaPrivateKey)
* @param string $rsaPrivateKey
* @return string
*/
public static function sign($message, $rsaPrivateKey)
public static function sign($message, PrivateKey $rsaPrivateKey)
{
$rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setMGFHash('sha256');

$rsa->loadKey($rsaPrivateKey);
$rsa->loadKey($rsaPrivateKey->getKey());
return $rsa->sign($message);
}

Expand All @@ -135,16 +121,16 @@ public static function sign($message, $rsaPrivateKey)
*
* @param string $message
* @param string $signature
* @param string $rsaPublicKey
* @param PublicKey $rsaPublicKey
* @return bool
*/
public static function verify($message, $signature, $rsaPublicKey)
public static function verify($message, $signature, PublicKey $rsaPublicKey)
{
$rsa = new RSA();
$rsa->setSignatureMode(RSA::SIGNATURE_PSS);
$rsa->setMGFHash('sha256');

$rsa->loadKey($rsaPublicKey);
$rsa->loadKey($rsaPublicKey->getKey());
return $rsa->verify($message, $signature);
}

Expand All @@ -155,13 +141,13 @@ public static function verify($message, $signature, $rsaPublicKey)
* @param string $rsaPublicKey
* @return string
*/
protected static function rsaEncrypt($plaintext, $rsaPublicKey)
protected static function rsaEncrypt($plaintext, PublicKey $rsaPublicKey)
{
$rsa = new RSA();
$rsa->setEncryptionMode(RSA::ENCRYPTION_OAEP);
$rsa->setMGFHash('sha256');

$rsa->loadKey($rsaPublicKey);
$rsa->loadKey($rsaPublicKey->getKey());
return $rsa->encrypt($plaintext);
}

Expand All @@ -172,13 +158,13 @@ protected static function rsaEncrypt($plaintext, $rsaPublicKey)
* @param string $rsaPrivateKey
* @return string
*/
protected static function rsaDecrypt($ciphertext, $rsaPrivateKey)
protected static function rsaDecrypt($ciphertext, PrivateKey $rsaPrivateKey)
{
$rsa = new RSA();
$rsa->setEncryptionMode(RSA::ENCRYPTION_OAEP);
$rsa->setMGFHash('sha256');

$rsa->loadKey($rsaPrivateKey);
$rsa->loadKey($rsaPrivateKey->getKey());

$return = @$rsa->decrypt($ciphertext);
if ($return === false) {
Expand Down
9 changes: 4 additions & 5 deletions src/EasyRSAInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@

interface EasyRSAInterface
{
public static function generateKeyPair();
public static function encrypt($plaintext, $rsaPublicKey);
public static function decrypt($ciphertext, $rsaPrivateKey);
public static function sign($plaintext, $rsaPrivateKey);
public static function verify($ciphertext, $signature, $rsaPublicKey);
public static function encrypt($plaintext, PublicKey $rsaPublicKey);
public static function decrypt($ciphertext, PrivateKey $rsaPrivateKey);
public static function sign($plaintext, PrivateKey $rsaPrivateKey);
public static function verify($ciphertext, $signature, PublicKey $rsaPublicKey);
}
58 changes: 58 additions & 0 deletions src/KeyPair.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
namespace ParagonIE\EasyRSA;

use \phpseclib\Crypt\RSA;
use \ParagonIE\EasyRSA\Exception\InvalidKeyException;

class KeyPair
{
private $privateKey;
protected $publicKey;

public function __construct(PrivateKey $privateKey, PublicKey $publicKey = null)
{
$this->privateKey = $privateKey;
if (!$publicKey) {
$publicKey = $this->privateKey->getPublicKey();
}
$this->publicKey = $publicKey;
}

/**
* Generate a private/public RSA key pair
*
* @param int $size Key size
* @param string $passphrase Optional - password-protected private key
*
* @return self
* @throws InvalidKeyException
*/
public static function generateKeyPair($size = 2048)
{
if ($size < 2048) {
throw new InvalidKeyException('Key size must be at least 2048 bits.');
}
$rsa = new RSA();
$keypair = $rsa->createKey($size);
return new KeyPair(
new PrivateKey($keypair['privatekey']),
new PublicKey($keypair['publickey'])
);
}

/**
* @return PublicKey
*/
public function getPublicKey()
{
return $this->publicKey;
}

/**
* @return PrivateKey
*/
public function getPrivateKey()
{
return $this->privateKey;
}
}
47 changes: 47 additions & 0 deletions src/PrivateKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php
namespace ParagonIE\EasyRSA;


class PrivateKey
{
protected $keyMaterial = '';

/**
* PrivateKey constructor.
* @param $string
*/
public function __construct($string)
{
$this->keyMaterial = $string;
}

/**
* @return array
*/
public function __debugInfo()
{
return [];
}

/**
* return PublicKey
*/
public function getPublicKey()
{
$res = \openssl_pkey_get_private($this->keyMaterial);
$pubkey = \openssl_pkey_get_details($res);
$public = \rtrim(
\str_replace("\n", "\r\n", $pubkey['key']),
"\r\n"
);
return new PublicKey($public);
}

/**
* @return string
*/
public function getKey()
{
return $this->keyMaterial;
}
}
32 changes: 32 additions & 0 deletions src/PublicKey.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php
namespace ParagonIE\EasyRSA;

class PublicKey
{
protected $keyMaterial = '';

/**
* PrivateKey constructor.
* @param $string
*/
public function __construct($string)
{
$this->keyMaterial = $string;
}

/**
* @return array
*/
public function __debugInfo()
{
return [];
}

/**
* @return string
*/
public function getKey()
{
return $this->keyMaterial;
}
}
Loading

0 comments on commit c36648a

Please sign in to comment.