Can't validate JWT token - go

All I wanted to do, is to generate a new secret key, create JWT token and then validate it.
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/base64"
"fmt"
"log"
"time"
"github.com/golang-jwt/jwt/v4"
)
func main() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatalln(err)
return
}
privateK, err := x509.MarshalECPrivateKey(key)
if err != nil {
log.Fatalln(err)
return
}
claims := jwt.MapClaims{}
claims["authorized"] = true
claims["user_id"] = 10
claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
t := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenStr, err := t.SignedString(key)
if err != nil {
log.Fatalln(err)
return
}
fmt.Printf("Secret: %s\n", base64.StdEncoding.EncodeToString(privateK))
fmt.Printf("Token: %s\n", tokenStr)
// Validate token
_, err = jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok {
return nil, fmt.Errorf("unexpected signing method %v", token.Header["alg"])
}
return key, nil
})
if err != nil {
log.Fatalf("Token is invalid %v", err)
} else {
fmt.Println("Token is valid")
}
}
And I get Token is invalid: key is of invalid type. What I'm doing wrong?

As per the docs
The ECDSA signing method (ES256,ES384,ES512) expect *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for validation
Your keyfunc is returning a *edcsa.PrivateKey which does not match the above. To fix this change return key, nil to return &key.PublicKey, nil (playground).

Related

Encrypt the message with the key

I need to use a public key to encrypt the message "Message" and save it as a file ciphertext.txt by converting pre-received data in HEX encoding. I do not need the public key to be generated, I already have a ready-made public key. Using this public key, you need to encrypt the message.
Here's what I was able to do:
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"log"
"os"
)
func main() {
publicKeyBytes, err := os.ReadFile("publicik.key")
if err!= nil {
return
}
publicKey, err := decodepublicKey(publicKeyBytes)
if err != nil {
return
}
plaintext := []byte("Message")
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, plaintext, nil)
if err != nil {
return
}
log.Printf( "%x", ciphertext)
privateKeyBytes, err := os.ReadFile("private.key")
if err != nil {
return
}
privateKey, err := decodePrivateKey(privateKeyBytes)
if err != nil {
return
}
decryptedtext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey,ciphertext, nil)
if err != nil {
return
}
log.Printf("%s", decryptedtext)
}
func decodepublicKey(key []byte) (*rsa.PublicKey, error) {
block, _:= pem.Decode(key)
if block == nil {
return nil, errors.New("can't decode pem block")
}
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
return nil, err
}
return publicKey, nil
}
func decodePrivateKey(key []byte) (*rsa.PrivateKey,error) { block, _ := pem.Decode(key)
if block == nil {
return nil, errors.New("can't decode pem block")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return privateKey, nil
}
Then I don’t know how to solve this problem?
Please help with solving this problem
I debugged your code and found 2 potential issues:
Double check whether you need to use x509.ParsePKCS1PublicKey() or x509.ParsePKIXPublicKey(). This depends on the format of your public key. More here: https://stackoverflow.com/a/54775627/9698467
You may need to type assert the public key in your decodepublicKey function: return publicKey.(*rsa.PublicKey), nil.

Error Decrypting test message with RSA/PEM files

Hi all I am currently trying to accomplish three things with the following code.
Generate a public/private key pair using the crypto/rsa library.
Export the public and private keys into individual PEM files to be used in separate programs.
Load the PEM files respectively into their individual scripts to encode/decode messages.
Everything works fine until I try to decrypt a test message with "Private-key-decryption.go". I received this error when decrypting the ciphertext "Error from decryption: crypto/rsa: decryption error"
Included are all of my code blocks I am using
Key-Generation.go
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
//Write_private_key_to_file write pem_key to a file
func WriteToFile(Pem_Key string, filename string) {
f, err := os.Create(filename)
if err != nil {
fmt.Println(err)
return
}
l, err := f.WriteString(Pem_Key)
if err != nil {
fmt.Println(err)
f.Close()
return
}
fmt.Println(l, "bytes written successfully")
err = f.Close()
if err != nil {
fmt.Println(err)
return
}
}
//ExportRsaPrivateKeyAsPemStr returns private pem key
func ExportRsaPrivateKeyAsPemStr(privkey *rsa.PrivateKey) string {
privkey_bytes := x509.MarshalPKCS1PrivateKey(privkey)
privkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privkey_bytes,
},
)
return string(privkey_pem)
}
//ExportRsaPublicKeyAsPemStr_to_pem_file extracts public key from generated private key
func ExportRsaPublicKeyAsPemStr(publickey *rsa.PublicKey) (string, error) {
pubkey_bytes, err := x509.MarshalPKIXPublicKey(publickey)
if err != nil {
return "", err
}
//fmt.Println(pubkey_bytes)
pubkey_pem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: pubkey_bytes,
},
)
return string(pubkey_pem), nil
}
func main() {
// generate a 1024-bit private-key
priv, err := rsa.GenerateKey(rand.Reader, 1024)
// extract the public key from the private key as string
pub := &priv.PublicKey
message := []byte("test message")
hashed := sha256.Sum256(message)
signature, err := rsa.SignPKCS1v15(rand.Reader, priv, crypto.SHA256, hashed[:])
if err != nil {
fmt.Printf("Error from signing: %s\n", err)
return
}
err = rsa.VerifyPKCS1v15(&priv.PublicKey, crypto.SHA256, hashed[:], signature)
if err != nil {
fmt.Printf("Error from verification: %s\n", err)
return
} else {
fmt.Printf("signature is verified\n")
}
//calling function to export private key into PEM file
pem_priv := ExportRsaPrivateKeyAsPemStr(priv)
//writing private key to file
WriteToFile(pem_priv, "private-key.pem")
//calling function to export public key as pPEM file
pem_pub, _ := ExportRsaPublicKeyAsPemStr(pub)
WriteToFile(pem_pub, "public-key.pem")
}
Public-key_encryption.go
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
//ParseRsaPublicKeyFromPemStr takes a publicKeyPEM file as a string and returns a rsa.PublicKey object
func ParseRsaPublicKeyFromPemStr(pubPEM string) (*rsa.PublicKey, error) {
block, _ := pem.Decode([]byte(pubPEM))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, err
}
switch pub := pub.(type) {
case *rsa.PublicKey:
return pub, nil
default:
break // fall through
}
return nil, errors.New("Key type is not RSA")
}
func main() {
//reading in the public key file to be passed the the rsa object creator
PublicKeyAsString, err := ioutil.ReadFile("public-key.pem")
if err != nil {
fmt.Print(err)
}
//Creating parsing Public PEM key to *rsa.PublicKey
rsa_public_key_object, _ := ParseRsaPublicKeyFromPemStr(string(PublicKeyAsString))
challengeMsg := []byte("c")
ciphertext, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, rsa_public_key_object, challengeMsg, nil)
if err != nil {
fmt.Printf("Error from encryption: %s\n", err)
return
}
fmt.Printf("%x", ciphertext)
}
Private-key-decryption.go
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
"io/ioutil"
)
//takes a privatekey PEM file as a string and returns a pointer rsa.PublicKey object
func parseRsaPrivateKeyFromPemStr(p string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(p))
if block == nil {
return nil, errors.New("failed to parse PEM block containing the key")
}
key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
return key, nil
}
func main() {
//reading in the public key file to be passed the the rsa object creator
PrivateKeyAsString, err := ioutil.ReadFile("private-key.pem")
if err != nil {
fmt.Print(err)
}
//Creating parsing private PEM key to *rsa.PublicKey
rsa_private_key_object, _ := parseRsaPrivateKeyFromPemStr(string(PrivateKeyAsString))
ciphertext := []byte("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
plaintext, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, rsa_private_key_object, ciphertext, nil)
if err != nil {
fmt.Printf("Error from decryption: %s\n", err)
return
}
fmt.Printf("\nPlaintext: %s\n", string(plaintext))
}
Please let me know what needs to be changed. This is my first crypto project and I am starting to get bags under my eyes lol
You're close. In the encryption part, you produce a hex string with that %x format string. So, in the decryption part, you should do the corresponding hex decode.
In your Private-key-decryption.go, change
ciphertext := []byte("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
to
ciphertext, err := hex.DecodeString("1f58ab29106c7971c9a4307c39b6b09f8910b7ac38a8d0abc15de14cbb0f651aa5c7ca377fd64a20017eaaff0a57358bc8dd05645c8b2b24bbb137ab2e5cf657f9a6a7593ce8d043dd774d79986b00f679fc1492a6ed4961f0e1941a5ef3c6ec99f952b0756700a05314c31c768fe9463f77f23312a51a97587b04b4d8b50de0")
if err != nil {
fmt.Printf("Error from hex decode: %s\n", err)
return
}

BOX / JWT OAuth 2.0 by golang

I want to upload image from my server without user. I made box application and setting.
I try to create JWT token and got access token. after that, I try to get file information from my Box file. but this api return 404 status.
I don't know where I missed. If you know, please help me.
My code is follow as.
package main
import (
"fmt"
"io/ioutil"
"time"
"encoding/json"
"github.com/dgrijalva/jwt-go"
"net/http"
"net/url"
"strings"
)
type BoxToken struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
RestrictedTo []string `json:"restricted_to"`
TokenType string `json:"token_type"`
}
func main() {
token := jwt.NewWithClaims(jwt.SigningMethodRS512, jwt.MapClaims{
"iss": "application client id",
"sub": "enterprise id",
"box_sub_type": "enterprise",
"aud": "https://api.box.com/oauth2/token",
"jti": "unique id",
"exp": time.Now().Unix() + 60,
})
token.Header["kid"] = "public key id"
privateKeyData, err := ioutil.ReadFile("private.key")
if err != nil {
panic(err)
}
key, err := jwt.ParseRSAPrivateKeyFromPEM(privateKeyData)
if err != nil {
panic(err)
}
// Generate encoded token and send it as response.
tokenStr, err := token.SignedString(key)
if err != nil {
panic(err)
}
//fmt.Println(tokenStr)
values := url.Values{}
values.Add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer")
values.Add("client_id", "application client id")
values.Add("client_secret", "application client secret")
values.Add("assertion", tokenStr)
req, err := http.NewRequest(http.MethodPost, "https://api.box.com/oauth2/token", strings.NewReader(values.Encode()))
if err != nil {
panic(err)
}
client := http.DefaultClient
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println(resp.StatusCode)
responseBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
var boxToken BoxToken
if err := json.Unmarshal(responseBody, &boxToken); err != nil {
panic(err)
}
req2, err := http.NewRequest("GET", "https://api.box.com/2.0/files/FILE_ID?fields=id,name", nil)
if err != nil {
panic(err)
}
req2.Header.Add("Authorization", `Bearer `+boxToken.AccessToken)
resp2, err := client.Do(req2)
if err != nil {
panic(err)
}
defer resp2.Body.Close()
fmt.Println(resp2.StatusCode)
responseBody2, err := ioutil.ReadAll(resp2.Body)
if err != nil {
panic(err)
}
fmt.Println(string(responseBody2))
}
and standard output is
404
{"type":"error","status":404,"code":"not_found","context_info":{"errors":[{"reason":"invalid_parameter","name":"item","message":"Invalid value 'f_${FILE_ID}'. 'item' with value 'f_${FILE_ID}' not found"}]},"help_url":"http:\/\/developers.box.com\/docs\/#errors","message":"Not Found","request_id":"3de39rftkndh2qqn"}
I believe that you need to actually pass an actual file id in place of "FILE_ID" in:
req2, err := http.NewRequest("GET", "https://api.box.com/2.0/files/FILE_ID?fields=id,name", nil)
Based on reading the API Reference

How to generate JWT token always through invalid key type error

I met this issue and really do not know how to resolve it, can anyone help to provide a working solution?
func GenerateJWT(name, role string) (string, error) {
//create a singner for rsa 256
claims := &jwt.StandardClaims{
ExpiresAt: 15000,
Issuer: "test",
}
token :=jwt.NewWithClaims(jwt.SigningMethodES256, claims)
log.Println("generated toke is ")
log.Println(token)
tokenString, err := token.SignedString([]byte("secret"))
....
}
Now I am alway having:
key is of invalid type
error. I google a lot, and even for jwt-go library it self, they are providing exactly same solution, but why I kept having the
key is of invalid type
error?
Can anyone help to provide a working sample about how to generate jwt token in go?
From the README:
The ECDSA signing method (ES256,ES384,ES512) expect *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for validation
So use an elliptic curve key:
package main
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"log"
jwt "github.com/dgrijalva/jwt-go"
)
func main() {
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
log.Fatal(err)
}
claims := &jwt.StandardClaims{
ExpiresAt: 15000,
Issuer: "test",
}
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
tokenString, err := token.SignedString(key)
if err != nil {
log.Fatal(err)
}
log.Println(tokenString)
}
To store the generated key for later use with jwt.ParseECPrivateKeyFromPEM and jwt.ParseECPublicKeyFromPEM:
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
)
func pemKeyPair(key *ecdsa.PrivateKey) (privKeyPEM []byte, pubKeyPEM []byte, err error) {
der, err := x509.MarshalECPrivateKey(key)
if err != nil {
return nil, nil, err
}
privKeyPEM = pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: der,
})
der, err = x509.MarshalPKIXPublicKey(key.Public())
if err != nil {
return nil, nil, err
}
pubKeyPEM = pem.EncodeToMemory(&pem.Block{
Type: "EC PUBLIC KEY",
Bytes: der,
})
return
}

Send email with attachments in golang

Here is the code:
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/user"
"path/filepath"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/gmail/v1"
"encoding/base64"
"io/ioutil"
)
// getClient uses a Context and Config to retrieve a Token
// then generate a Client. It returns the generated Client.
func getClient(ctx context.Context, config *oauth2.Config, configFileName string) *http.Client {
cacheFile, err := tokenCacheFile(configFileName)
if err != nil {
log.Fatalf("Unable to get path to cached credential file. %v", err)
}
tok, err := tokenFromFile(cacheFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(cacheFile, tok)
}
return config.Client(ctx, tok)
}
// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the " +
"authorization code: \n%v\n", authURL)
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}
tok, err := config.Exchange(oauth2.NoContext, code)
if err != nil {
log.Fatalf("Unable to retrieve token from web %v", err)
}
return tok
}
// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func tokenCacheFile(filename string) (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
os.MkdirAll(tokenCacheDir, 0700)
return filepath.Join(tokenCacheDir,
url.QueryEscape(filename)), err
}
// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
t := &oauth2.Token{}
err = json.NewDecoder(f).Decode(t)
defer f.Close()
return t, err
}
// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", file)
f, err := os.Create(file)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
func main() {
// Use oauth2.NoContext if there isn't a good context to pass in.
//ctx := context.TODO()
ctx := context.Background()
b, err := ioutil.ReadFile("client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-go-quickstart.json
sendConfig, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
sendClient := getClient(ctx, sendConfig, "send.json")
sendService, err := gmail.New(sendClient)
if err != nil {
log.Fatalf("Unable to retrieve gmail Client %v", err)
}
if err := SendEmail(ctx, sendService, "jane1988#gmail.com"); err != nil {
log.Fatalf("failed to send email: %v", err)
}
}
func SendEmail(ctx context.Context, svc *gmail.Service, email string) error {
header := make(map[string]string)
header["To"] = email
header["Subject"] = "hello there"
header["MIME-Version"] = "1.0"
header["Content-Type"] = `text/html; charset="utf-8"`
header["Content-Transfer-Encoding"] = "base64"
var msg string
for k, v := range header {
msg += fmt.Sprintf("%s: %s\n", k, v)
}
msg += "\n" + "Hello, Gmail!"
gmsg := gmail.Message{
Raw: encodeWeb64String([]byte(msg)),
}
_, err := svc.Users.Messages.Send("me", &gmsg).Do()
return err
}
func encodeWeb64String(b []byte) string {
s := base64.URLEncoding.EncodeToString(b)
var i = len(s) - 1
for s[i] == '=' {
i--
}
return s[0 : i + 1]
}
This works perfectly, but without attachments. How can I attach files to the mail?
Maybe you can try change the header Content-Type to multipart/mixed (RFC 2046, Section 5.1.3) or multipart/alternative (RFC 2046, Section 5.1.4) and check how to use Content-Disposition: attachment; filename=<your file here.ext>.

Resources