Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang signing a struct using rsa

I have a struct Transaction with the following fields,

type Transaction struct {
    Sender    string `json:"sender"`
    Receiver  string `json:"receiver"`
    Signature string `json:"signature"`
    Amount    int64  `json:"amount"`
}

I also have a function GetPrivateKey() which returns a *rsa.PrivateKey

func GetPrivateKey() (*rsa.PrivateKey, error) {
    key, err := ioutil.ReadFile("/Users/xxx/.ssh/id_rsa")
    if err != nil {
        return nil, err
    }

    block, _ := pem.Decode(key)
    der, err := x509.ParsePKCS1PrivateKey(block.Bytes)
    if err != nil {
       return nil, err
    }
    return der, err
 }

My plan here is to sign the contents of the struct transaction using a private key already in the system and then store it as a string in the field signature in the struct, For this, I have a function SignPayload()

func SignPayload(txnObj *Transaction) error {
    privateKey, err := GetPrivateKey()
    if err != nil {
        panic(err)
    }
    bodyHash, err := rsa.SignPKCS1v15(rand.Reader, privateKey, 
                     crypto.SHA256, []byte(fmt.Sprintf("%v", *txnObj)))
    if err != nil {
        panic(err)
    }
    txnObj.Signature = string(bodyHash)
    log.Println(txnObj.Signature)
    return err
}

Unfortunately, this is not working,rsa.SignPKCS1v15() throws an error saying:

crypto/rsa: input must be hashed message

The end goal here is to use this signature to verify the authenticity of the struct by comparing it with the public key present in the field sender. I'm an absolute newbie in cryptography, What am I doing wrong here? and more importantly, How can I do this?

Thanks in advance!

like image 412
ashishmax31 Avatar asked Feb 11 '18 10:02

ashishmax31


1 Answers

Two things here:

  • only small messages can be signed, which is why it's usually a hash. rsa.SignPKCS1v15 expects a message length matching the hash algorithm's output (256 bits, or 32 bytes for sha-256)
  • don't use the %v representation of your struct, serialize it instead

The first is spelled out in the rsa docs. Given a message, you can sign it using the following:

message := []byte("message to be signed")
hashed := sha256.Sum256(message)

signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
if err != nil {
    panic(err)}
}

The second point is more about making your code more robust. You don't want a change in %v logic or addition of non-exported fields to cause issues. You may also want to check the signature from a language other than Go.

Thus, you should first marshal your struct. You already have json tags on it, so it's fairly easy:

message, err := json.Marshal(txnObj)
if err != nil {
    panic(err)
}

Message is the byte slice passed to sha256.Sum256 above.

When verifying the signature, you'll need to make sure to zero out the Signature field of your struct, marshal it, hash it, and call rsa.VerifyPKCS1v15.

like image 138
Marc Avatar answered Oct 19 '22 13:10

Marc