How to turn *rsa.PublicKey into a recognisable key string - go

I have this function:
func GetSigningKey() *rsa.PublicKey {
set, _ := jwk.ParseString(GetWellKnown())
publicKey, _ := set.Keys[0].Materialize()
return publicKey.(*rsa.PublicKey)
}
.Materialize() returns interface{}, so I use this function to cast it to (what I think is) the expected type.
I can then use that token with:
publicKey := GetSigningKey()
token, _ := jwt.Parse(tokenString, func(*jwt.Token) (interface{}, error) {
return publicKey, nil
})
fmt.Println(token.Valid)
A few questions:
The purpose of the GetSigningKey function is to obtain the signing key from a well known file and cast the resulting key to the correct type.
1) This requires an http request which lives within GetWellKnown(). In that function, I do the http request and cache the response in a variable so following requests will pull from that variable. Is this a good practice? (follow up, should I do the same thing to the parsed public key in GetSigningKey())
2) Am I right in assuming that this is the best way to cast the interface{} to the correct type?
3) if I try fmt.Println(GetSigningKey()) I get a long, unrecognisable sequence of numbers
&{2568409364095596940117712027335... %!s(int=65537)}635 <nil>
What exactly am I seeing here?

There's no error checking in your code. Especially if the JWKS is empty or contains something that's not an RSA key, your program will just crash, and that tends to not be the behavior encouraged in Go. I'd add some checking around the whole thing; for instance,
func GetSigningKey() *rsa.PublicKey, error {
...
if rsaKey, ok := publicKey.(*rsa.PublicKey); ok {
return rsaKey, nil
}
return nil, errors.New("not an RSA key")
}
Remember that other signing methods exist and in principle you can get back an ECDSA key or (in theory) a raw key for symmetric signing, so checking here is valuable.
The other high-level thing you should try is to take the raw JSON Web token and decode it; https://jwt.io/ has a debugger I use. If the header contains a claim named "kid", that is a key ID, and you should be able to pass that key ID as an input to the JWKS library. That should be more robust than blindly using the first key in the JWKS.
Online JWKS documents can change, but seem to generally do so infrequently (on a scale of months). (If they do change, it's likely that a JWKS will include both old and new keys.) Caching them in-process is extremely reasonable. If you see a "kid" you don't recognize that can be a signal to try refetching the document.
Your fmt.Printf output looks like it's probably the raw RSA public key. The public key itself consists of two numbers, one being the product of two large prime numbers and the second frequently being exactly 65537. There is some more discussion of this in the Wikipedia description of RSA.

Related

2 parameters invoke lambda aws from golang

i want to send the 2 parameters a lambda needs in order to work and it basically needs the value i want to search and as a second parameter the field where to find that value.
Now with no problem i've been able to access some other lambdas with that only need one parameter with a code like this.
func (s *resourceService) GetProject(ctx context.Context, name string) projectStruct {
payload, err := json.Marshal(name)
util.Logger.Debugf("Payload",payload)
invokeOutput, err := s.lambdaSvc.Invoke(ctx, &lambda.InvokeInput{
FunctionName: &s.getProject,
InvocationType: "RequestResponse",
Payload: payload,
})
if err != nil {
panic(err.Error())
}
var project projectStruct
err = json.Unmarshal(invokeOutput.Payload, &project)
if err != nil {
panic(err.Error())
}
util.Logger.Debugf("Invocation output [%v]", invokeOutput)
return project
}
now with 2 parameters i've had a lot of problems and tried a LOT of different approaches starting for adding another Payload value, creating a string with the 2 values and marshal it, marshaling both parameters and try and add them as the payload, even append both marshaled bytes array but i've been incapable of sending 2 parameters as the payload
Do you know the right way to do so? Please Help
Lambda functions only take one Payload. In V1 of the AWS SDK, InvokeInput takes one []byte parameter expressing JSON, as you already know.
You can structure your one Json Payload to carry a list. Looking at your example, Payload could look something like
["name","name"]
You could change your signature like so:
func (s *resourceService) GetProject(ctx context.Context, names []string) projectStruct
json.Marshal can handle marshaling a slice just as well as the elements within the slice, so the remaining code doesn't need to change.
Of course the receiving function must agree about the schema of the data it's being passed. If you wish to change from a string to a list of strings, that will be a breaking change. For that reason, Json schemas typically use named values instead of scalars.
[{ "Name": "Joan"},{"Name":"Susan"}]
You can add Age and Address without breaking the receiving function (though of course, it will ignore the new fields until you program it to ignore them).
Take time to Get to know JSON - it's a simple and expressive encoding standard that is reliably supported everywhere. JSON is a natural choice for encoding structured data in Go because JSON integrates well with Go's with structures, maps, and slices.

Failing to create JWT token when entering a custom email claim

I am trying to generate a JWT token using Go and I created the following function. I need to add the email address in jwt but as I do this I get an error saying key is of invalid type
func GenerateUserToken(expiryHours time.Duration, email string, secretKey string) (string, error) {
// Create a new token object, specifying signing method and the claims
// you would like it to contain.
token := jwt.New(jwt.SigningMethodES256)
claims := token.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * expiryHours).Unix()
claims["email"] = email
tokenStr, err := token.SignedString([]byte(secretKey))
if err != nil {
return "", err
}
return tokenStr, nil
}
What could be the reason for this? What mistake am I making?
JWT supports many signing algorithms, and that's a challenge for this particular API: depending on the signing algorithm, it expects to see a key matching that algorithm.
If you take a look at the API docs for this particular library:
https://godoc.org/github.com/dgrijalva/jwt-go
You'll see SigningMethodXXX types. These are signers selected by the signing method you pick. For ES256, it uses SigningMethodECDSA:
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA
If you look at the Sign method docs, you'll see that it says:
For this signing method, key must be an ecdsa.PrivateKey struct
which you can parse from a PEM file using:
https://godoc.org/github.com/dgrijalva/jwt-go#ParseECPrivateKeyFromPEM
For example:
pk, err:= jwt.ParseECPrivateKeyFromPEM(pemData)
tokenStr, err := token.SignedString(pk)
This should give you a signed token with ES256.
So, you have to first figure out what kind of key you have. If you have a PEM encoding of a ECDSA key in a string, then use this method to parse it and pass the resulting private key to the signer.
If however you simply have a string secret key (like a password) and you'll share this secret key with the users of the JWT, then you can use a HMAC key. A HMAC key is simply a byte array secret that you share with your users so they can validate that the JWT was signed by you. Simply change the SigningMethod to one of the constants in:
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC
Then, your code as it is will work with the exception that you have to change the signing method to something like jwt.New(jwt.SigningMethodHS256)

When create JWT token inside loop getting same token in jwt-go

I am creating jwt tokens using jwt-go library. Later wrote a script to load test. I have noticed when I send the many concurrent request getting same token. To check more about this I created token inside for loop and result is same.
The library that I use is https://github.com/dgrijalva/jwt-go, go version is 1.12.9.
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
for i := 1; i < 5; i++ {
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer:"telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
}
A JWT contains three parts: a mostly-fixed header, a set of claims, and a signature. RFC 7519 has the actual details. If the header is fixed and the claims are identical between two tokens, then the signature will be identical too, and you can easily get duplicated tokens. The two timestamp claims "iat" and "exp" are only at a second granularity, so if you issue multiple tokens with your code during the same second you will get identical results (even if you move the expirationTime calculation inside the loop).
The jwt-go library exports the StandardClaims listed in RFC 7519 ยง4.1 as a structure, which is what you're using in your code. Digging through the library code, there's nothing especially subtle here: StandardClaims uses ordinary "encoding/json" annotations, and then when a token is written out, the claims are JSON encoded and then base64-encoded. So given a fixed input, you'll get a fixed output.
If you want every token to be "different" in some way, the standard "jti" claim is a place to provide a unique ID. This isn't part of the StandardClaims, so you need to create your own custom claim type that includes it.
type UniqueClaims struct {
jwt.StandardClaims
TokenId string `json:"jti,omitempty"`
}
Then when you create the claims structure, you need to generate a unique TokenId yourself.
import (
"crypto/rand"
"encoding/base64"
)
bits := make([]byte, 12)
_, err := rand.Read(bits)
if err != nil {
panic(err)
}
claims := UniqueClaims{
StandardClaims: jwt.StandardClaims{...},
TokenId: base64.StdEncoding.EncodeToString(bits),
}
https://play.golang.org/p/zDnkamwsCi- has a complete example; every time you run it you will get a different token, even if you run it multiple times in the same second. You can base64 decode the middle part of the token by hand to see the claims, or use a tool like the https://jwt.io/ debugger to decode it.
I changed your code:
Moved calculation of expirationTime in the loop
Added 1 sec delay on each step of loop
for i := 1; i < 5; i++ {
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer: "telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
time.Sleep(time.Duration(1) * time.Second)
}
In this case we get different tokens:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDgsImlzcyI6InRlbGVzY29wZSJ9.G7wV-zsCYjysLEdgYAq_92JGDPsgqqOz9lZxdh5gcX8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDksImlzcyI6InRlbGVzY29wZSJ9.yPNV20EN3XJbGiHhe-wGTdiluJyVHXj3nIqEsfwDZ0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTAsImlzcyI6InRlbGVzY29wZSJ9.W3xFXEiVwh8xK47dZinpXFpKuvUl1LFUAiaLZZzZ2L0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTEsImlzcyI6InRlbGVzY29wZSJ9.wYUbzdXm_VQGdFH9RynAVVouW9h6KI1tHRFJ0Y322i4
Sorry, I am not big expert in JWT and I hope somebody who is explain us this behavior from RFC point of view.
I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens.
It is the same user and we can get him the same token. If we want to give it another one we need to revoke previous one or the client must refresh it.

Verifying signature of payload in Go

I am verifying the identity of the sender of a piece of data. I am provided the RSA public key in a PEM format and I know the data is passed through the SHA256 hashing function. The equivalent verification on the node.js platform:
Ticket.prototype.verify = function (ticket) {
if (!ticket) return null;
var pubkey = fs.readFileSync('/etc/SCAMP/auth/ticket_verify_public_key.pem');
var parts = ticket.split(',');
if (parts[0] != '1') return null;
var sig = new Buffer(parts.pop().replace(/-/g,'+').replace(/_/g,'/'), 'base64');
var valid = crypto.createVerify('sha256').update( new Buffer(parts.join(',')) ).verify( pubkey, sig )
Which can verify:
1,3063,21,1438783424,660,1+20+31+32+34+35+36+37+38+39+40+41+42+43+44+46+47+48+50+53+56+59+60+61+62+67+68+69+70+71+75+76+80+81+82+86+87+88+102+104+105+107+109+110+122+124,PcFNyWjoz_iiVMgEe8I3IBfzSlUcqUGtsuN7536PTiBW7KDovIqCaSi_8nZWcj-j1dfbQRA8mftwYUWMhhZ4DD78-BH8MovNVucbmTmf2Wzbx9bsI-dmUADY5Q2ol4qDXG4YQJeyZ6f6F9s_1uxHTH456QcsfNxFWh18ygo5_DVmQQSXCHN7EXM5M-u2DSol9MSROeBolYnHZyE093LgQ2veWQREbrwg5Fcp2VZ6VqIC7yu6f_xYHEvU0-ZsSSRMAMUmhLNhmFM4KDjl8blVgC134z7XfCTDDjCDiynSL6b-D-
by splitting on the last ,. The left side of the split is the ticket data I care about, the right side is the signature which I need to verify before I can use the ticket data.
I have tried to port the logic to go:
func TestSigVerification(t *testing.T) {
block, _ := pem.Decode(signingPubKey)
if block == nil {
t.Errorf("expected to block to be non-nil CERTIFICATE", block)
}
key, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
t.Errorf("could not parse PKIXPublicKey: `%s`", key)
}
rsaPubKey, ok := key.(*rsa.PublicKey)
if !ok {
t.Errorf("couldn't cast to rsa.PublicKey!")
}
ticket,_ := ParseTicketBytes(fullTicketBytes)
h := sha256.New()
h.Write(ticketBytes)
digest := h.Sum(nil)
err = rsa.VerifyPKCS1v15(rsaPubKey, crypto.SHA256, digest, ticket.Signature)
if err != nil {
t.Errorf("could not verify ticket: `%s` (digest: `%v`)", err, digest )
}
}
But I'm pretty sure VerifyPKCS1v15 is not equivalent to node's crypto.createVerify and this test case fails. What should I be using? How can I use the public key to decrypt the signature and get the sha256? once I have the decrypted sha256 value I could just do a basic comparison with the sha256 I have generated.
Here's a runnable playground example: http://play.golang.org/p/COx2OG-AiA
Though I couldn't get it to work, I suspect the issue is that you'll need to convert the sig from base64 into bytes via the base64 encoding. See this example here:
http://play.golang.org/p/bzpD7Pa9mr (especially lines 23 to 28, where they have to encode the sig from bytes to base64 string to print it, then feed the byte version into the sig check, indicating that you have to use the byte version and not base64 string)
Which I stumbled across on this post:
Signing and decoding with RSA-SHA in GO
I've found that golang generally expects bytes everywhere in byte encoding. I tried to decode your sig string from base64 to bytes however, even after replacing the '-' with '+' and the '_' with '/' it still won't work, for reasons unknown to me:
http://play.golang.org/p/71IiV2z_t8
At the very least this seems to indicate that maybe your sig is bad? If it isn't valid base64? I think if you can find a way to solve this the rest should work!
I tried running your code and it doesn't look like your ticket is well formed. It's not long enough. Where did you get the value from?
Double check that ticket -- that may just be enough to get you going.

CryptoJS.AES and Golang

i already managed to share a random symetric key via rsa.
However i fail to make aes-encryption work with it.
Problem seems to be the salt and initialization vector, that cryptoJS uses.
First it's output is along the lines of:
U2FsdGVkX19wbzVqqOr6U5dnuG34WyH+n1A4PX9Z+xBhY3bALGS7DOa/aphgnubc
Googling for the reoccuring "U2FsdGVkX" or "cryptoJS.AES output" sadly is of no use.
On the other hand, golang's aes requires only a 32bit key and input of 32bit length each.
Which means i have to somehow split the above into the corresponding blocks and figure out, how to create the 32bit key out of the secret key and the data above (which proably includes salt + init vector).
Sadly neither http://code.google.com/p/crypto-js nor any google search provide me with a solution.
By the way - my encryption right now:
var arr = new Array(32);
symetricKey = "";
var symHex = "";
rng.nextBytes(arr);
for(var i = 0; i < arr.length; i++){
symetricKey += String.fromCharCode(arr[i]);
//symHex= arr[i].toString(16), added a 0 if needed (so length always increases by 2)
}
//submit symetric via rsa. This is working, the server gets that key
var enc = CryptoJS.AES.encrypt(unencrypted, symetricKey)
//submit enc, stuck now - what to do with it on the server?
Edit: After the Base64 response:
Thanks for the base64 input.
However i still don't manage to bring it to work.
Especially since the encoded string starts with "SALTED",
i believe, that there might be a problem.
Way i try to encode now:
Encoded on Client by:
var unencrypted = "{mail:test,password:test}"
var enc = CryptoJS.AES.encrypt(unencrypted, symKey)
On Server, the variables enc and symKey are the same as on Client:
baseReader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(enc))
encData, err := ioutil.ReadAll(baseReader)
//if err != nil { ....}, doesn't happen here
cipher, err := aes.NewCipher(symKey)
//if err != nil { ....}, doesn't happen here
byteData := make([]byte, len(encData))
cipher.Decrypt(byteData, encData)
fmt.Println("Dec: ", string(byteData))
//Outputs unrepresentable characters
Any idea?
The output of CryptoJS.AES.encrypt is a CipherParams object containing the key, IV, optional salt, and ciphertext. The string you were referencing was a OpenSSL-compatible formatted string.
var encrypted = CryptoJS.AES.encrypt("Message", "Secret Passphrase");
alert(encrypted.key); // 74eb593087a982e2a6f5dded54ecd96d1fd0f3d44a58728cdcd40c55227522223
alert(encrypted.iv); // 7781157e2629b094f0e3dd48c4d786115
alert(encrypted.salt); // 7a25f9132ec6a8b34
alert(encrypted.ciphertext); // 73e54154a15d1beeb509d9e12f1e462a0
alert(encrypted); // U2FsdGVkX1+iX5Ey7GqLND5UFUoV0b7rUJ2eEvHkYqA= (OpenSSL-compatible format strategy)
CryptoJS' default encryption mode is CBC. You should pass the IV along with your symmetric key during your RSA-encrypted exchange. With the symmetric key, IV, and cipher text byte arrays on the server, you can decrypt it in Go similar to this:
c, err := aes.NewCipher(key)
cfbdec := cipher.NewCBCDecrypter(c, iv)
plaintext := make([]byte, len(ciphertext))
cfbdec.CryptBlock(plaintext, ciphertext)
U2FsdGVkX19wbzVqqOr6U5dnuG34WyH+n1A4PX9Z+xBhY3bALGS7DOa/aphgnubc
That data is base64 encoded, raw it looks something like this:
Salted__po5jSgm[!P8=Yacv,dj`
(note that there are unrepresentable characters in that string)
And this is exactly 32 bytes long.
Although, it don't directly but maybe you will find it useful to have a look at https://github.com/dgryski/dkeyczar.
Its Go implementation of KeyCzar, open source cryptographic toolkit originally developed by members of the Google Security Team
I hope you can learn something from this project
There are probably a few issues we'll need to work through.
First, you need to make sure you're supplying the correct type of arguments to encrypt(). It looks like you may have realized in a comment that you need to do CryptoJS.enc.Hex.parse(symetricKeyHex), though your original post doesn't reflect this yet.
Second, you need to decide how you're getting or creating the IV on both ends. CryptoJS can create one if you use a passphrase, but you're trying to use an actual key rather than a passphrase. That means you'll have to set the IV like you set the key.
Finally, you'll have to decide how you're going to transmit both the IV and the ciphertext. A typical and simple solution is to prepend the IV to the ciphertext, but really anything that's easily serializable/unserializable will work fine. Just make sure that whatever format you decide on is consistent on both ends.

Resources