Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RSA Signing & Verification Support #35

Closed
wants to merge 38 commits into from

Conversation

liamdennehy
Copy link
Contributor

@liamdennehy liamdennehy commented Aug 17, 2018

RSA signing is largely similar to hmac, both requiring a secret (shared in the hmac case), but the difference in key material for signing is significant. Verification is very different, so not trivial to extend.

This PR adds:

  • Explicitly typing RSA private keys as distinct from secrets (hmac)
  • Importing keys and certificates into the KeyStore
  • Detecting whether to use the RSA signing flow depending on the value of algorithm field, and selecting the right hash algorithm (could be replaced on detection of supplied key?)
  • Detecting whether to use the RSA verification flow depending on the type of key retrieved with keyId and use a X.509 certificate instead of a secret/privateKey.

@liamdennehy liamdennehy changed the title RSA Signatures support, for all SHA2 variants RSA Signing Support Aug 18, 2018
src/RsaAlgorithm.php Outdated Show resolved Hide resolved
@liamdennehy liamdennehy changed the title RSA Signing Support RSA Signing & Verification Support Aug 19, 2018
@liamdennehy
Copy link
Contributor Author

OK, that's it. I think this is ready for a thorough review, after handling importing RSA keys & certificates for singing and verification respectively, with a good amount unit tests for all sorts of variations.

src/Algorithm.php Outdated Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved
src/Key.php Outdated
$this->secret = $item;
$publicKey = null;
$privateKey = null;
};
Copy link
Member

@mtibben mtibben Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you're specifying a very different representation of a key here - if the implementation is so different I'd suggest the concrete types be split out and use a common interface

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This took me ages to decide on. I wasn't happy just inventing a new architectural component, and burdening the user with having to understand the various key types with explicit calls. This implementation doesn't break existing flows, and automagically sorts the asymmetric types to handle the sign() and verify() flows (which don't differ in the hmac case).

Acknowledged this is not a complete implementation, but is that preferred over compatibility and user experience?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're looking at an overhaul of the Key type and KeyStore, should we incorporate #18 for the KeyStore itself? Or maybe handle the proposed work under that issue in the future and implement this solution for now?

Copy link
Member

@mtibben mtibben Aug 20, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK so I'd suggest approaching this a little bit differently. Keep the single concrete Key and keep it a simple representation of the Key. Ensure your secret is a string only - mixed use variables are a pain IMO. Remove the public variables but expose a

public function isRSAPrivateKey()
public function isX509Certificate()

Then in Verification and *Algorithm put the actual logic

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RSA there is no one "key", there is a signing key (private) and a verification key (public), so we need to handle these. Keep the internal representation but private, expose them via functions as you describe? Oh, and a certificate is just a container for a public key, we should use/expose it directly for consistency.

public function getRSAPublicKey()
public function getRSAPrivateKey()
public function getHmacSecret()

For EC (in the future) add:

public function getECPublicKey()
public function getECPrivateKey()

And with key overhaul to handle rotation in #18 (also future):

public function getRSAPublicKeys($keyId)
public function getECPublicKeys($keyId)
public function getHmacSecrets($keyId)

Copy link
Contributor Author

@liamdennehy liamdennehy Aug 22, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generalised to:

public function getSigningKey()
public function getVerifyingKey()

Internal state for a key is now private, and the calls return the right public/private or secret for sign/verify. b9ae276

In future this response can be an array under a single keyId to handle key rotation in #18

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtibben, can you review the overhaul? I think it's a lot cleaner and tolerant of different input types (e.g. array of possible verification keys or just a single key).
b9ae276

liamdennehy added a commit to liamdennehy/http-signatures-php that referenced this pull request Aug 20, 2018
src/Context.php Outdated Show resolved Hide resolved
src/Signature.php Outdated Show resolved Hide resolved
@liamdennehy
Copy link
Contributor Author

Any further comments here?

@mtibben
Copy link
Member

mtibben commented Oct 2, 2018

Can you rebase and remove the merge commits?

src/Context.php Outdated Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved
src/RsaAlgorithm.php Outdated Show resolved Hide resolved
src/Verification.php Outdated Show resolved Hide resolved
@mtibben
Copy link
Member

mtibben commented Oct 4, 2018

Hey @liamdennehy I'm not sure you're following my request regarding the rebase, so will try be more clear :) We follow a "feature branch" or "Github Flow" workflow. So:

  1. We don't want merge commits. Rebase them out
  2. We want your branch to be from the most recent master commit. Rebase on master

@liamdennehy
Copy link
Contributor Author

@mtibben There's a strong possibility I have no idea what I'm doing ☺️
Thanks for the clarification.

liamdennehy added a commit to liamdennehy/http-signatures-php that referenced this pull request Oct 4, 2018
src/Context.php Outdated Show resolved Hide resolved
@@ -34,9 +37,8 @@ public function __construct($args)

// algorithm for signing; not necessary for verifying.
if (isset($args['algorithm'])) {
$this->algorithmName = $args['algorithm'];
$this->algorithm = Algorithm::create($args['algorithm']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason for this change?

Copy link
Contributor Author

@liamdennehy liamdennehy Oct 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This old parameter only handled the HMAC case, where a signing and verifying key are the same. Needed a class to discriminate HMAC from RSA, so invoke with a factory that makes the right underlying Algorithm subclass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtibben Still new to OO, is this a good approach?

src/Key.php Outdated Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved

private function getRSAPrivateKey($object)
{
if (is_array($object)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under what circumstances is this going to be an array and why? Ideally avoid allowing multiple types in arguments

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The user either supplies:

  • A Public Key/Certificate for verifying as string/object
  • A Private Key for signing as string/object
  • Or both together in an array of string/objects - this can now be used for both signing and verifying without having to construct different objects for each case.

Allowing both forms means we can use the same KeyStore object for all three cases to derive a Key object. Alternative is to expose HMAC and Asymmetric-specific Key interfaces which is both a BC and requires more documentation and user education for little gain.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mtibben Is this ok? I realise the input here is fungible, but I'd rather that than expect the user to do key/certificate handling to a more advanced degree.

src/Key.php Show resolved Hide resolved
src/Key.php Outdated Show resolved Hide resolved
@liamdennehy
Copy link
Contributor Author

@mtibben I'm going to overhaul the Key class to make the flow easier to track, eg

    static function hasX509Certificate($object)

...to explicitly test if an object has a certificate instead of try..catch blocks all over the place.

This will also make a good base to extend the class for #18 (Key Rotation). Will ping you when it's ready.

* Key now handles inputs as X.509, naked Public Key and
  Private Key (all PEM)
* Improved & clearer detection logic with explicit tests
  for asymmetric key material, falling back on shared secret
  for HMAC if asymmetric keys are not detected
  hasX509Certificate()
  hasPublicKey()
  hasPrivateKey()
* Clearer names for test constants (easier to expand to EC later)
@liamdennehy
Copy link
Contributor Author

@mtibben Hopefully much clearer Key class in b9ae276, and quicker than I thought.

@liamdennehy
Copy link
Contributor Author

Any further questions, or updates on open conversations in this PR?

@lahaxearnaud
Copy link

Hello,

Can you please merge and tag this PR ?

Regards,

Arnaud

liamdennehy added a commit to liamdennehy/http-signatures-php that referenced this pull request Jul 30, 2019
@liamdennehy liamdennehy deleted the rsa branch July 31, 2019 14:28
@liamdennehy liamdennehy restored the rsa branch August 2, 2019 14:05
@liamdennehy liamdennehy reopened this Aug 2, 2019
@liamdennehy
Copy link
Contributor Author

liamdennehy commented Aug 2, 2019

Apologies, accidentally deleted the branch for this PR in my source.

However, I have integrated this feature in my own project along with a number of others, and published this in packagist. Documentation for the entire library is published at Read the Docs: http-signatures-php - incomplete but being expanded regularly.

- Remove unsupported Symfony/PHP version combinations
- Add PHP 7.3 support
@liamdennehy
Copy link
Contributor Author

Closing PR due to inactivity.

@liamdennehy liamdennehy deleted the rsa branch August 15, 2019 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants