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

Abstract signing #8

Closed
ghstahl opened this issue Oct 6, 2019 · 10 comments
Closed

Abstract signing #8

ghstahl opened this issue Oct 6, 2019 · 10 comments
Labels
enhancement New feature or request

Comments

@ghstahl
Copy link

ghstahl commented Oct 6, 2019

Feature Request.
I can make a call to Azure keyvault to sign and what I get back are the following ;

  1. kid
  2. base64 url encoded signature
  3. alg, i.e. "RSA256" passed in

Could you think about putting the signing stuff into an interface and letting me implement that on my end. I would still expect your code to construct the JWT ({0}.{1}.{signature})

That way we could also reduce what looks like redundant code in all the *Sign() functions.

my POC function looks like this, and would change based upon the interface requirements of your engine;

// HARD CODED to SHA256 at the moment

func RSA256AzureSign(ctx context.Context, data []byte) (kid *string, signature *string, err error) {
	keyClient := getKeysClient()
	digest := crypto.SHA256.New()
	digest.Write(data)
	h := digest.Sum(nil)
	sEnc := b64.StdEncoding.EncodeToString(h)

	keyOperationResult, err := keyClient.Sign(ctx, "https://P7KeyValut.vault.azure.net/", "P7IdentityServer4SelfSigned", "", keyvault.KeySignParameters{
		Algorithm: keyvault.RS256,
		Value:     &sEnc,
	})
	if err != nil {
		return
	}
	return keyOperationResult.Kid, keyOperationResult.Result, nil
}

The call to azure picks the latest version of the cert and signs for me, returning the kid and the base64 signature.

@pascaldekloe
Copy link
Owner

The problem with this setup is that both the key ID ("kid") and the algorithm ("alg") are part of the signed data ({0}). Thus first choose a key & algorithm, then produce the data, and then the signature.

I'm having a bit of a hard time with understanding why you'd want the service invoker to be in control of such detail? Maybe use the audience ("aud") claim to choose server-side which sign logic to apply?

@pascaldekloe pascaldekloe added the enhancement New feature or request label Oct 7, 2019
@pascaldekloe
Copy link
Owner

You probably mean this https://docs.microsoft.com/en-us/rest/api/keyvault/sign/sign feature, right?

So you provide the key, the algorithm plus data. The returned key ID is not only late, but also too long for a JWT "kid" value. Other than that, we can make this work. Maybe add the call to this library too, in a subpackage?

We already have a func ParseWithoutCheck(token []byte) (*Claims, error). Wouldn't be much of a stretch to add func (*Claims) FormatWithoutSign(alg string) (tokenWithoutSignature []byte, err error).

@ghstahl
Copy link
Author

ghstahl commented Oct 7, 2019

I can provide you with the KID and ALG up front. That is something I can get by calling azure, determine what KID I will use as I always have 2 up there for key rotation. I have a cron running to pull new keybundles that will become active in the future. I can change the kid based upon its activation date being reached. "RSA256" will be determined by me and it is something you can get at via an interface.

type Signer interface {
	Sign(payload []byte) (signature *string, err error)
        GetSigningAttributes()(kid *string, alg *string)
}

If I could implement this on my end then I could control only what I absolutely need to control. I still would not want to build the JWT myself. I just want to sign.

btw here is an access_token that I generate using the IdentityServer4 (dotnetcore) stuff. Its got a FAT ole KID in there and that is not something I can control as those KIDS come from keyvault directly.

Anyway, works fine being big

What do you think of that abstraction?

@pascaldekloe
Copy link
Owner

The interface is more of an enterprise (dotNet, Java) design which requires an object per algorithm and enforces strings for runtime data. The exact same could be accomplished with a more generic method—no custom types needed.

claims.KeyID = myKid
tokenWithoutSignature, err := claims.FormatWithoutSign(myAlg)
if err != nil {
        …
}
token := append(tokenWithoutSignature, mySig...)

Second, all that could be abstracted in a subpackage as func NewAzureClient(baseURL string) AzureClient, with func (*AzureClient) Sign(*jwt.Claims, keyName, keyVersion string) (token []byte, error). A little collaboration and we'll end up with something nice and reusable. What does your client look like? How does Azure handle autz?

@ghstahl
Copy link
Author

ghstahl commented Oct 7, 2019

Ok, let's go your way.

I am on windows, but that shouldn't matter

https://github.com/Azure-Samples/azure-sdk-for-go-samples/tree/master/keyvault
https://github.com/Azure-Samples/azure-sdk-for-go-samples

I am currently using the example key.go as a starting point.
They use env.go and config.go to read in this config and build the keyvault client

I put the following in my environment
export AZURE_SUBSCRIPTION_ID=
export AZURE_TENANT_ID=
export AZURE_CLIENT_ID=
export AZURE_CLIENT_SECRET=

I don't know why the go keyvault client needs subscription_id and tenant_id, because the dotnetcore client doesn't. I think that they can derive it from the vault url.

let me know if it would be better for me to direct message you so temporary creds to my dev azure keyvault. It was pretty easy to setup keyvault and get certs to rotate.

my stuff is here.
https://github.com/P7CoreOrg/artificer/blob/master/pkg/keyvault/key.go
to get my /.well-known/openid-configuration/jwks data for a minimal OAuth2 project I am working on it call GetActiveKeysVersion() to fetch the public keys from azure.

I will checkin the signing code tonight.
This is all POC stuff at the moment.

The artificer project will work when there is a good keyvault with certs.

@pascaldekloe
Copy link
Owner

Oegh! That Azure SDK is as confusing and ugly as Amazon's. 🤮 Fortunately there's a YouTuber who made sense of it all.
https://youtu.be/fh37VQ3_exk We need all 4 of those settings you mentioned indeed.

@pascaldekloe
Copy link
Owner

Tonight I'll give the OAuth2 REST a try. Can you share those 4 variables with me @ghstahl on [email protected], please?

@pascaldekloe
Copy link
Owner

Token production without signature done. ✔️ Ready for Azure when you are @ghstahl

pascaldekloe added a commit that referenced this issue Nov 4, 2019
* Performance loss on HTTPS, OAuth2 and JSON overhead.
* No security improvement with cart-blanche sign design.
* Microsoft's OAuth2 implementation is broken.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants