I fetched from the first certificate on: https://www.googleapis.com/oauth2/v2/certs the 'n' and 'e' key values. Is there a package in Go that can build a public key with 'n' and 'e'? I don't know how it's done using the crypto/rsa package. Some code would be precious. Thank You.
The rsa package has a PublicKey type with fields N and E. It should be pretty straightforward to decode the parts as described in the JWA draft.
Here is some quickly hacked code (Playground):
package main
import (
"bytes"
"crypto/rsa"
"encoding/base64"
"encoding/binary"
"fmt"
"math/big"
)
func main() {
nStr := "AN+7p8kw1A3LXfAJi+Ui4o8F8G0EeB4B5RuufglWa4AkadDaLTxGLNtY/NtyRZBfwhdAmRjKQJTVgn5j3y0s+j/bvpzMktoVeHB7irOhxDnZJdIxNNMY3nUKBgQB81jg8lNTeBrJqELSJiRXQIe5PyWJWwQJ1XrtfQNcwGkICM1L"
decN, err := base64.StdEncoding.DecodeString(nStr)
if err != nil {
fmt.Println(err)
return
}
n := big.NewInt(0)
n.SetBytes(decN)
eStr := "AQAB"
decE, err := base64.StdEncoding.DecodeString(eStr)
if err != nil {
fmt.Println(err)
return
}
var eBytes []byte
if len(decE) < 8 {
eBytes = make([]byte, 8-len(decE), 8)
eBytes = append(eBytes, decE...)
} else {
eBytes = decE
}
eReader := bytes.NewReader(eBytes)
var e uint64
err = binary.Read(eReader, binary.BigEndian, &e)
if err != nil {
fmt.Println(err)
return
}
pKey := rsa.PublicKey{N: n, E: int(e)}
}
Related
If I have a map, I can Marshal it no problem:
package main
import (
"encoding/json"
"os"
)
type object map[int]interface{}
func main() {
obj := object{
1: "one", 2: object{3: "three"},
}
buf, err := json.Marshal(obj)
if err != nil {
panic(err)
}
os.Stdout.Write(buf) // {"1":"one","2":{"3":"three"}}
}
However I want to do the reverse. I tried this:
package main
import (
"encoding/json"
"fmt"
)
func main() {
buf := []byte(`{"1":"one","2":{"3":"three"}}`)
var obj map[int]interface{}
json.Unmarshal(buf, &obj)
// map[int]interface {}{1:"one", 2:map[string]interface {}{"3":"three"}}
fmt.Printf("%#v\n", obj)
}
Only the top level has the correct type. Is it possible to do what I am wanting?
JSON keys are never anything but strings, that's how the spec is defined, and you can see that in the output of you marshal. So when you try the reverse with interface{} as the top level map's value type, the type information for the nested objects is lost. You'd need a custom map type that implements UnmarshalJSON to be able to do what you want.
For example:
type IntKeyMap map[int]interface{}
func (m *IntKeyMap) UnmarshalJSON(data []byte) error {
raw := map[int]json.RawMessage{}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
for k, v := range raw {
// check if the value is a nested object
if len(v) > 0 && v[0] == '{' && v[len(v)-1] == '}' {
// The following assumes that the nested JSON object's
// key strings represent integers, if that is not the
// case this block will fail.
obj := IntKeyMap{}
if err := json.Unmarshal([]byte(v), &obj); err != nil {
return err
}
(*m)[k] = obj
} else {
var i interface{}
if err := json.Unmarshal([]byte(v), &i); err != nil {
return err
}
(*m)[k] = i
}
}
return nil
}
https://go.dev/play/p/lmyhqD__Uod
I have a JSON file in S3 that takes the format of the following struct:
type StockInfo []struct {
Ticker string `json:"ticker"`
BoughtPrice string `json:"boughtPrice"`
NumberOfShares string `json:"numberOfShares"`
}
and I want to read it into a struct value from S3. In python the code would look something like this:
import boto3
import json
s3 = boto3.client('s3', 'us-east-1')
obj = s3.get_object(Bucket=os.environ["BucketName"], Key=os.environ["Key"])
fileContents = obj['Body'].read().decode('utf-8')
json_content = json.loads(fileContents)
However I'm kinda stuck on how to make this happen in Go. I've gotten this far:
package main
import (
"archive/tar"
"bytes"
"fmt"
"log"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/s3/s3manager"
"github.com/joho/godotenv"
)
type StockInfo []struct {
Ticker string `json:"ticker"`
BoughtPrice string `json:"boughtPrice"`
NumberOfShares string `json:"numberOfShares"`
}
func init() {
// loads values from .env into the system
if err := godotenv.Load(); err != nil {
log.Print("No .env file found")
}
return
}
func main() {
// Store the PATH environment variable in a variable
sess, err := session.NewSession(&aws.Config{
Region: aws.String("us-east-1")},
)
if err != nil {
panic(err)
}
s3Client := s3.New(sess)
bucket := "ian-test-bucket-go-python"
key := "StockInfo.json"
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
result, err := s3Client.GetObject(requestInput)
if err != nil {
fmt.Println(err)
}
fmt.Println(result)
which returns to me the body/object buffer, but im not sure how to read that into a string so I can marshal it into my struct. I found this code in a similar question:
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
buf := new(aws.WriteAtBuffer)
numBytes, _ := *s3manager.Downloader.Download(buf, requestInput)
tr := tar.NewReader(bytes.NewReader(buf.Bytes()))
but I get the following errors:
not enough arguments in call to method expression s3manager.Downloader.Download
have (*aws.WriteAtBuffer, *s3.GetObjectInput)
want (s3manager.Downloader, io.WriterAt, *s3.GetObjectInput, ...func(*s3manager.Downloader))
multiple-value s3manager.Downloader.Download() in single-value context
Can anyone point me in the right direction? kinda frustrating how hard it seems to do this compared to python.
I was able to do it with the following code:
requestInput := &s3.GetObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
}
result, err := s3Client.GetObject(requestInput)
if err != nil {
fmt.Println(err)
}
defer result.Body.Close()
body1, err := ioutil.ReadAll(result.Body)
if err != nil {
fmt.Println(err)
}
bodyString1 := fmt.Sprintf("%s", body1)
var s3data StockInfo
decoder := json.NewDecoder(strings.NewReader(bodyString1))
err = decoder.Decode(&s3data)
if err != nil {
fmt.Println("twas an error")
}
fmt.Println(s3data)
Alternative solution using json.Unmarshal
besed on aws-sdk-go-v2
...
params := &s3.GetObjectInput{
Bucket: aws.String(s3Record.S3.Bucket.Name),
Key: aws.String(s3Record.S3.Object.Key),
}
result, _ := client.GetObject(context.TODO(), params)
if err != nil {
panic(err)
}
defer result.Body.Close()
// capture all bytes from upload
b, err := ioutil.ReadAll(result.Body)
if err != nil {
panic(err)
}
var temp StockInfo
if err = json.Unmarshal(b, &temp); err != nil {
panic(err)
}
ftm.Println("res: ",b)
I'm trying to give permissions to users to edit certain fields of a struct.
This fields will vary depending on the action, the context, and the role the current user has.
At the moment I'm doing this in an imperative way but it's rather tedious and not scalable.
I thought having a list of approved_fields could be a nice and more scalable solution but I have no idea how to go through with this. I think reflection is the way to go but I don't know enough yet.
Any inspiration would help.
type Foo struct {
Bar1 int
Bar2 int
Bar3 int
}
foo := Foo{}
approved_fields := []string{"Bar1", "Bar2"}
decode(json_data, &foo, approved_fields)
foo2 := Foo{}
approved_fields := []string{"Bar1", "Bar3"}
decode(json_data, &foo2, approved_fields)
Here's how I would think about solving it. There may be something more efficient since I am unmarshalling the whole item before selectively choosing fields.
import (
"encoding/json"
"log"
"reflect"
"github.com/pkg/errors"
)
func fieldByName(s string, v interface{}) (reflect.Value, error) {
el := reflect.ValueOf(v).Elem()
fbn := el.FieldByName(s)
if !fbn.IsValid() {
return fbn, errors.Errorf("does not have field named %s", s)
}
return fbn, nil
}
func decode(data []byte, v interface{}, approvedFields []string) error {
typeOf := reflect.TypeOf(v)
if typeOf.Kind() == reflect.Ptr {
typeOf = typeOf.Elem()
}
if typeOf.Kind() == reflect.Slice {
return errors.New("does not support slices")
}
newItem := reflect.New(typeOf)
newItemInterface := newItem.Interface()
if err := json.Unmarshal(data, &newItemInterface); err != nil {
return errors.Wrap(err, "json unmarshall")
}
for _, approvedField := range approvedFields {
fbn, err := fieldByName(approvedField, v)
if err != nil {
return errors.Wrap(err, "field by name")
}
val, _ := fieldByName(approvedField, newItemInterface)
fbn.Set(val)
}
return nil
}
a test:
func TestBar1Bar2(t *testing.T) {
var json_data []byte
{
f := &Foo{
Bar1: 1,
Bar2: 2,
Bar3: 3,
}
json_data, _ = json.Marshal(f)
}
approved_fields := []string{"Bar1", "Bar2"}
f := &Foo{}
err := decode(json_data, f, approved_fields)
if err != nil {
t.Fatal(err)
}
assert.Equal(t, f.Bar1, 1)
assert.Equal(t, f.Bar2, 2)
assert.Equal(t, f.Bar3, 0)
}
I am trying to build an API, but to secure it properly I believe I need to go with RSA encryption for a private key stored on my server and a public key for the client. I have stored the generated private key into a JSON file, I plan to store on my server but to write to JSON, I needed to convert the type too []byte. Now when I try to retrieve the private key to generate a public key, but it will not let me use type bytes for *Publickey
The only other way I can think of to accomplish this goal is to seed the random number generator, so I can have the seed a secret on my server and then my private key should always generate to the same thing, any help this this would be great.
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
func main() {
mimicPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
buf := new(bytes.Buffer)
json.NewEncoder(buf).Encode(mimicPrivateKey)
secrets, _ := os.OpenFile("secrets.json", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666)
// Close the secrets file when the surrounding function exists
secrets.WriteString(buf.String())
secrets.Close()
secrets, _ = os.OpenFile("secrets.json", os.O_RDWR, 0666)
serverKey, _ := ioutil.ReadAll(secrets)
if serverKey != nil {
fmt.Println("can not open key")
}
serverKeyPublic := &serverKey.PublicKey
}
You need to Unmarshal it:
var data *rsa.PrivateKey
err = json.Unmarshal(serverKey, &data)
if err != nil {
panic(err)
}
And you may use
err = ioutil.WriteFile("secrets.json", buf.Bytes(), 0666)
and
serverKey, err := ioutil.ReadFile("secrets.json")
See:
package main
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"encoding/json"
"fmt"
"io/ioutil"
)
func main() {
mimicPrivateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
var buf bytes.Buffer
err = json.NewEncoder(&buf).Encode(mimicPrivateKey)
if err != nil {
panic(err)
}
err = ioutil.WriteFile("secrets.json", buf.Bytes(), 0666)
if err != nil {
panic(err)
}
serverKey, err := ioutil.ReadFile("secrets.json")
if err != nil {
panic(err)
}
var data *rsa.PrivateKey
err = json.Unmarshal(serverKey, &data)
if err != nil {
panic(err)
}
serverKeyPublic := data.PublicKey
fmt.Println(serverKeyPublic)
}
map[key:2073933158088]
I need to grab the key out of this data structure as a string, but I can't seem to figure out how!
Help with this overly simple question very much appreciated.
The value above is encapsulated in the variable named data.
I have tried: data.key, data[key], data["key"], data[0] and none of these seem to be appropriate calls.
To define data I sent up a JSON packet to a queue on IronMQ. I then pulled the message from the queue and manipulated it like this:
payloadIndex := 0
for index, arg := range(os.Args) {
if arg == "-payload" {
payloadIndex = index + 1
}
}
if payloadIndex >= len(os.Args) {
panic("No payload value.")
}
payload := os.Args[payloadIndex]
var data interface{}
raw, err := ioutil.ReadFile(payload)
if err != nil {
panic(err.Error())
}
err = json.Unmarshal(raw, &data)
Design your data type to match json structure. This is how can you achieve this:
package main
import (
"fmt"
"encoding/json"
)
type Data struct {
Key string `json:"key"`
}
func main() {
data := new(Data)
text := `{ "key": "2073933158088" }`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
Since the number in the json is unquoted, it's not a string, Go will panic if you try to just handle it as a string (playground: http://play.golang.org/p/i-NUwchJc1).
Here's a working alternative:
package main
import (
"fmt"
"encoding/json"
"strconv"
)
type Data struct {
Key string `json:"key"`
}
func (d *Data) UnmarshalJSON(content []byte) error {
var m map[string]interface{}
err := json.Unmarshal(content, &m)
if err != nil {
return err
}
d.Key = strconv.FormatFloat(m["key"].(float64), 'f', -1, 64)
return nil
}
func main() {
data := new(Data)
text := `{"key":2073933158088}`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
You can see the result in the playground: http://play.golang.org/p/5hU3hdV3kK